/*

 * jQuery timepicker addon

 * By: Trent Richardson [http://trentrichardson.com]

 * Version 1.1.2

 * Last Modified: 01/19/2013

 *

 * Copyright 2012 Trent Richardson

 * You may use this project under MIT or GPL licenses.

 * http://trentrichardson.com/Impromptu/GPL-LICENSE.txt

 * http://trentrichardson.com/Impromptu/MIT-LICENSE.txt

 */



/*jslint evil: true, white: false, undef: false, nomen: false */



(function($) {



	/*

	* Lets not redefine timepicker, Prevent "Uncaught RangeError: Maximum call stack size exceeded"

	*/

	$.ui.timepicker = $.ui.timepicker || {};

	if ($.ui.timepicker.version) {

		return;

	}



	/*

	* Extend jQueryUI, get it started with our version number

	*/

	$.extend($.ui, {

		timepicker: {

			version: "1.1.2"

		}

	});



	/* 

	* Timepicker manager.

	* Use the singleton instance of this class, $.timepicker, to interact with the time picker.

	* Settings for (groups of) time pickers are maintained in an instance object,

	* allowing multiple different settings on the same page.

	*/

	function Timepicker() {

		this.regional = []; // Available regional settings, indexed by language code

		this.regional[''] = { // Default regional settings

			currentText: 'Now',

			closeText: 'Done',

			amNames: ['AM', 'A'],

			pmNames: ['PM', 'P'],

			timeFormat: 'HH:mm',

			timeSuffix: '',

			timeOnlyTitle: 'Choose Time',

			timeText: 'Time',

			hourText: 'Hour',

			minuteText: 'Minute',

			secondText: 'Second',

			millisecText: 'Millisecond',

			timezoneText: 'Time Zone',

			isRTL: false

		};

		this._defaults = { // Global defaults for all the datetime picker instances

			showButtonPanel: true,

			timeOnly: false,

			showHour: true,

			showMinute: true,

			showSecond: false,

			showMillisec: false,

			showTimezone: false,

			showTime: true,

			stepHour: 1,

			stepMinute: 1,

			stepSecond: 1,

			stepMillisec: 1,

			hour: 0,

			minute: 0,

			second: 0,

			millisec: 0,

			timezone: null,

			useLocalTimezone: false,

			defaultTimezone: "+0000",

			hourMin: 0,

			minuteMin: 0,

			secondMin: 0,

			millisecMin: 0,

			hourMax: 23,

			minuteMax: 59,

			secondMax: 59,

			millisecMax: 999,

			minDateTime: null,

			maxDateTime: null,

			onSelect: null,

			hourGrid: 0,

			minuteGrid: 0,

			secondGrid: 0,

			millisecGrid: 0,

			alwaysSetTime: true,

			separator: ' ',

			altFieldTimeOnly: true,

			altTimeFormat: null,

			altSeparator: null,

			altTimeSuffix: null,

			pickerTimeFormat: null,

			pickerTimeSuffix: null,

			showTimepicker: true,

			timezoneIso8601: false,

			timezoneList: null,

			addSliderAccess: false,

			sliderAccessArgs: null,

			controlType: 'slider',

			defaultValue: null,

			parse: 'strict'

		};

		$.extend(this._defaults, this.regional['']);

	}



	$.extend(Timepicker.prototype, {

		$input: null,

		$altInput: null,

		$timeObj: null,

		inst: null,

		hour_slider: null,

		minute_slider: null,

		second_slider: null,

		millisec_slider: null,

		timezone_select: null,

		hour: 0,

		minute: 0,

		second: 0,

		millisec: 0,

		timezone: null,

		defaultTimezone: "+0000",

		hourMinOriginal: null,

		minuteMinOriginal: null,

		secondMinOriginal: null,

		millisecMinOriginal: null,

		hourMaxOriginal: null,

		minuteMaxOriginal: null,

		secondMaxOriginal: null,

		millisecMaxOriginal: null,

		ampm: '',

		formattedDate: '',

		formattedTime: '',

		formattedDateTime: '',

		timezoneList: null,

		units: ['hour','minute','second','millisec'],

		control: null,



		/* 

		* Override the default settings for all instances of the time picker.

		* @param  settings  object - the new settings to use as defaults (anonymous object)

		* @return the manager object

		*/

		setDefaults: function(settings) {

			extendRemove(this._defaults, settings || {});

			return this;

		},



		/*

		* Create a new Timepicker instance

		*/

		_newInst: function($input, o) {

			var tp_inst = new Timepicker(),

				inlineSettings = {},

                fns = {},

		        overrides, i;



			for (var attrName in this._defaults) {

				if(this._defaults.hasOwnProperty(attrName)){

					var attrValue = $input.attr('time:' + attrName);

					if (attrValue) {

						try {

							inlineSettings[attrName] = eval(attrValue);

						} catch (err) {

							inlineSettings[attrName] = attrValue;

						}

					}

				}

			}

		    overrides = {

		        beforeShow: function (input, dp_inst) {

		            if ($.isFunction(tp_inst._defaults.evnts.beforeShow)) {

		                return tp_inst._defaults.evnts.beforeShow.call($input[0], input, dp_inst, tp_inst);

		            }

		        },

		        onChangeMonthYear: function (year, month, dp_inst) {

		            // Update the time as well : this prevents the time from disappearing from the $input field.

		            tp_inst._updateDateTime(dp_inst);

		            if ($.isFunction(tp_inst._defaults.evnts.onChangeMonthYear)) {

		                tp_inst._defaults.evnts.onChangeMonthYear.call($input[0], year, month, dp_inst, tp_inst);

		            }

		        },

		        onClose: function (dateText, dp_inst) {

		            if (tp_inst.timeDefined === true && $input.val() !== '') {

		                tp_inst._updateDateTime(dp_inst);

		            }

		            if ($.isFunction(tp_inst._defaults.evnts.onClose)) {

		                tp_inst._defaults.evnts.onClose.call($input[0], dateText, dp_inst, tp_inst);

		            }

		        }

		    };

		    for (i in overrides) {

		        if (overrides.hasOwnProperty(i)) {

		            fns[i] = o[i] || null;

		        }

		    }

		    tp_inst._defaults = $.extend({}, this._defaults, inlineSettings, o, overrides, {

		        evnts:fns,

		        timepicker: tp_inst // add timepicker as a property of datepicker: $.datepicker._get(dp_inst, 'timepicker');

		    });

			tp_inst.amNames = $.map(tp_inst._defaults.amNames, function(val) {

				return val.toUpperCase();

			});

			tp_inst.pmNames = $.map(tp_inst._defaults.pmNames, function(val) {

				return val.toUpperCase();

			});



			// controlType is string - key to our this._controls

			if(typeof(tp_inst._defaults.controlType) === 'string'){

				if($.fn[tp_inst._defaults.controlType] === undefined){

					tp_inst._defaults.controlType = 'select';

				}

				tp_inst.control = tp_inst._controls[tp_inst._defaults.controlType];

			}

			// controlType is an object and must implement create, options, value methods

			else{ 

				tp_inst.control = tp_inst._defaults.controlType;

			}



			if (tp_inst._defaults.timezoneList === null) {

				var timezoneList = ['-1200', '-1100', '-1000', '-0930', '-0900', '-0800', '-0700', '-0600', '-0500', '-0430', '-0400', '-0330', '-0300', '-0200', '-0100', '+0000', 

									'+0100', '+0200', '+0300', '+0330', '+0400', '+0430', '+0500', '+0530', '+0545', '+0600', '+0630', '+0700', '+0800', '+0845', '+0900', '+0930', 

									'+1000', '+1030', '+1100', '+1130', '+1200', '+1245', '+1300', '+1400'];



				if (tp_inst._defaults.timezoneIso8601) {

					timezoneList = $.map(timezoneList, function(val) {

						return val == '+0000' ? 'Z' : (val.substring(0, 3) + ':' + val.substring(3));

					});

				}

				tp_inst._defaults.timezoneList = timezoneList;

			}



			tp_inst.timezone = tp_inst._defaults.timezone;

			tp_inst.hour = tp_inst._defaults.hour < tp_inst._defaults.hourMin? tp_inst._defaults.hourMin : 

							tp_inst._defaults.hour > tp_inst._defaults.hourMax? tp_inst._defaults.hourMax : tp_inst._defaults.hour;

			tp_inst.minute = tp_inst._defaults.minute < tp_inst._defaults.minuteMin? tp_inst._defaults.minuteMin : 

							tp_inst._defaults.minute > tp_inst._defaults.minuteMax? tp_inst._defaults.minuteMax : tp_inst._defaults.minute;

			tp_inst.second = tp_inst._defaults.second < tp_inst._defaults.secondMin? tp_inst._defaults.secondMin : 

							tp_inst._defaults.second > tp_inst._defaults.secondMax? tp_inst._defaults.secondMax : tp_inst._defaults.second;

			tp_inst.millisec = tp_inst._defaults.millisec < tp_inst._defaults.millisecMin? tp_inst._defaults.millisecMin : 

							tp_inst._defaults.millisec > tp_inst._defaults.millisecMax? tp_inst._defaults.millisecMax : tp_inst._defaults.millisec;

			tp_inst.ampm = '';

			tp_inst.$input = $input;



			if (o.altField) {

				tp_inst.$altInput = $(o.altField).css({

					cursor: 'pointer'

				}).focus(function() {

					$input.trigger("focus");

				});

			}



			if (tp_inst._defaults.minDate === 0 || tp_inst._defaults.minDateTime === 0) {

				tp_inst._defaults.minDate = new Date();

			}

			if (tp_inst._defaults.maxDate === 0 || tp_inst._defaults.maxDateTime === 0) {

				tp_inst._defaults.maxDate = new Date();

			}



			// datepicker needs minDate/maxDate, timepicker needs minDateTime/maxDateTime..

			if (tp_inst._defaults.minDate !== undefined && tp_inst._defaults.minDate instanceof Date) {

				tp_inst._defaults.minDateTime = new Date(tp_inst._defaults.minDate.getTime());

			}

			if (tp_inst._defaults.minDateTime !== undefined && tp_inst._defaults.minDateTime instanceof Date) {

				tp_inst._defaults.minDate = new Date(tp_inst._defaults.minDateTime.getTime());

			}

			if (tp_inst._defaults.maxDate !== undefined && tp_inst._defaults.maxDate instanceof Date) {

				tp_inst._defaults.maxDateTime = new Date(tp_inst._defaults.maxDate.getTime());

			}

			if (tp_inst._defaults.maxDateTime !== undefined && tp_inst._defaults.maxDateTime instanceof Date) {

				tp_inst._defaults.maxDate = new Date(tp_inst._defaults.maxDateTime.getTime());

			}

			tp_inst.$input.bind('focus', function() {

				tp_inst._onFocus();

			});



			return tp_inst;

		},



		/*

		* add our sliders to the calendar

		*/

		_addTimePicker: function(dp_inst) {

			var currDT = (this.$altInput && this._defaults.altFieldTimeOnly) ? this.$input.val() + ' ' + this.$altInput.val() : this.$input.val();



			this.timeDefined = this._parseTime(currDT);

			this._limitMinMaxDateTime(dp_inst, false);

			this._injectTimePicker();

		},



		/*

		* parse the time string from input value or _setTime

		*/

		_parseTime: function(timeString, withDate) {

			if (!this.inst) {

				this.inst = $.datepicker._getInst(this.$input[0]);

			}



			if (withDate || !this._defaults.timeOnly) {

				var dp_dateFormat = $.datepicker._get(this.inst, 'dateFormat');

				try {

					var parseRes = parseDateTimeInternal(dp_dateFormat, this._defaults.timeFormat, timeString, $.datepicker._getFormatConfig(this.inst), this._defaults);

					if (!parseRes.timeObj) {

						return false;

					}

					$.extend(this, parseRes.timeObj);

				} catch (err) {

					console.log("Error parsing the date/time string: " + err +

									"\ndate/time string = " + timeString +

									"\ntimeFormat = " + this._defaults.timeFormat +

									"\ndateFormat = " + dp_dateFormat);

					return false;

				}

				return true;

			} else {

				var timeObj = $.datepicker.parseTime(this._defaults.timeFormat, timeString, this._defaults);

				if (!timeObj) {

					return false;

				}

				$.extend(this, timeObj);

				return true;

			}

		},



		/*

		* generate and inject html for timepicker into ui datepicker

		*/

		_injectTimePicker: function() {

			var $dp = this.inst.dpDiv,

				o = this.inst.settings,

				tp_inst = this,

				litem = '',

				uitem = '',

				max = {},

				gridSize = {},

				size = null;



			// Prevent displaying twice

			if ($dp.find("div.ui-timepicker-div").length === 0 && o.showTimepicker) {

				var noDisplay = ' style="display:none;"',

					html = '<div class="ui-timepicker-div'+ (o.isRTL? ' ui-timepicker-rtl' : '') +'"><dl>' + '<dt class="ui_tpicker_time_label"' + ((o.showTime) ? '' : noDisplay) + '>' + o.timeText + '</dt>' + 

								'<dd class="ui_tpicker_time"' + ((o.showTime) ? '' : noDisplay) + '></dd>';



				// Create the markup

				for(var i=0,l=this.units.length; i<l; i++){

					litem = this.units[i];

					uitem = litem.substr(0,1).toUpperCase() + litem.substr(1);

					// Added by Peter Medeiros:

					// - Figure out what the hour/minute/second max should be based on the step values.

					// - Example: if stepMinute is 15, then minMax is 45.

					max[litem] = parseInt((o[litem+'Max'] - ((o[litem+'Max'] - o[litem+'Min']) % o['step'+uitem])), 10);

					gridSize[litem] = 0;



					html += '<dt class="ui_tpicker_'+ litem +'_label"' + ((o['show'+uitem]) ? '' : noDisplay) + '>' + o[litem +'Text'] + '</dt>' + 

								'<dd class="ui_tpicker_'+ litem +'"><div class="ui_tpicker_'+ litem +'_slider"' + ((o['show'+uitem]) ? '' : noDisplay) + '></div>';



					if (o['show'+uitem] && o[litem+'Grid'] > 0) {

						html += '<div style="padding-left: 1px"><table class="ui-tpicker-grid-label"><tr>';



						if(litem == 'hour'){

							for (var h = o[litem+'Min']; h <= max[litem]; h += parseInt(o[litem+'Grid'], 10)) {

								gridSize[litem]++;

								var tmph = $.datepicker.formatTime(useAmpm(o.pickerTimeFormat || o.timeFormat)? 'hht':'HH', {hour:h}, o);									

								html += '<td data-for="'+litem+'">' + tmph + '</td>';

							}

						}

						else{

							for (var m = o[litem+'Min']; m <= max[litem]; m += parseInt(o[litem+'Grid'], 10)) {

								gridSize[litem]++;

								html += '<td data-for="'+litem+'">' + ((m < 10) ? '0' : '') + m + '</td>';

							}

						}



						html += '</tr></table></div>';

					}

					html += '</dd>';

				}

				

				// Timezone

				html += '<dt class="ui_tpicker_timezone_label"' + ((o.showTimezone) ? '' : noDisplay) + '>' + o.timezoneText + '</dt>';

				html += '<dd class="ui_tpicker_timezone" ' + ((o.showTimezone) ? '' : noDisplay) + '></dd>';



				// Create the elements from string

				html += '</dl></div>';

				var $tp = $(html);



				// if we only want time picker...

				if (o.timeOnly === true) {

					$tp.prepend('<div class="ui-widget-header ui-helper-clearfix ui-corner-all">' + '<div class="ui-datepicker-title">' + o.timeOnlyTitle + '</div>' + '</div>');

					$dp.find('.ui-datepicker-header, .ui-datepicker-calendar').hide();

				}

				

				// add sliders, adjust grids, add events

				for(var i=0,l=tp_inst.units.length; i<l; i++){

					litem = tp_inst.units[i];

					uitem = litem.substr(0,1).toUpperCase() + litem.substr(1);



					// add the slider

					tp_inst[litem+'_slider'] = tp_inst.control.create(tp_inst, $tp.find('.ui_tpicker_'+litem+'_slider'), litem, tp_inst[litem], o[litem+'Min'], max[litem], o['step'+uitem]);



					// adjust the grid and add click event

					if (o['show'+uitem] && o[litem+'Grid'] > 0) {

						size = 100 * gridSize[litem] * o[litem+'Grid'] / (max[litem] - o[litem+'Min']);

						$tp.find('.ui_tpicker_'+litem+' table').css({

							width: size + "%",

							marginLeft: o.isRTL? '0' : ((size / (-2 * gridSize[litem])) + "%"),

							marginRight: o.isRTL? ((size / (-2 * gridSize[litem])) + "%") : '0',

							borderCollapse: 'collapse'

						}).find("td").click(function(e){

								var $t = $(this),

									h = $t.html(),

									n = parseInt(h.replace(/[^0-9]/g),10),

									ap = h.replace(/[^apm]/ig),

									f = $t.data('for'); // loses scope, so we use data-for



								if(f == 'hour'){

									if(ap.indexOf('p') !== -1 && n < 12){

										n += 12;

									}

									else{

										if(ap.indexOf('a') !== -1 && n === 12){

											n = 0;

										}

									}

								}

								

								tp_inst.control.value(tp_inst, tp_inst[f+'_slider'], litem, n);



								tp_inst._onTimeChange();

								tp_inst._onSelectHandler();

							})

						.css({

								cursor: 'pointer',

								width: (100 / gridSize[litem]) + '%',

								textAlign: 'center',

								overflow: 'hidden'

							});

					} // end if grid > 0

				} // end for loop



				// Add timezone options

				this.timezone_select = $tp.find('.ui_tpicker_timezone').append('<select></select>').find("select");

				$.fn.append.apply(this.timezone_select,

				$.map(o.timezoneList, function(val, idx) {

					return $("<option />").val(typeof val == "object" ? val.value : val).text(typeof val == "object" ? val.label : val);

				}));

				if (typeof(this.timezone) != "undefined" && this.timezone !== null && this.timezone !== "") {

					var local_date = new Date(this.inst.selectedYear, this.inst.selectedMonth, this.inst.selectedDay, 12);

					var local_timezone = $.timepicker.timeZoneOffsetString(local_date);

					if (local_timezone == this.timezone) {

						selectLocalTimeZone(tp_inst);

					} else {

						this.timezone_select.val(this.timezone);

					}

				} else {

					if (typeof(this.hour) != "undefined" && this.hour !== null && this.hour !== "") {

						this.timezone_select.val(o.defaultTimezone);

					} else {

						selectLocalTimeZone(tp_inst);

					}

				}

				this.timezone_select.change(function() {

					tp_inst._defaults.useLocalTimezone = false;

					tp_inst._onTimeChange();

					tp_inst._onSelectHandler();

				});

				// End timezone options

				

				// inject timepicker into datepicker

				var $buttonPanel = $dp.find('.ui-datepicker-buttonpane');

				if ($buttonPanel.length) {

					$buttonPanel.before($tp);

				} else {

					$dp.append($tp);

				}



				this.$timeObj = $tp.find('.ui_tpicker_time');



				if (this.inst !== null) {

					var timeDefined = this.timeDefined;

					this._onTimeChange();

					this.timeDefined = timeDefined;

				}



				// slideAccess integration: http://trentrichardson.com/2011/11/11/jquery-ui-sliders-and-touch-accessibility/

				if (this._defaults.addSliderAccess) {

					var sliderAccessArgs = this._defaults.sliderAccessArgs,

						rtl = this._defaults.isRTL;

					sliderAccessArgs.isRTL = rtl;

						

					setTimeout(function() { // fix for inline mode

						if ($tp.find('.ui-slider-access').length === 0) {

							$tp.find('.ui-slider:visible').sliderAccess(sliderAccessArgs);



							// fix any grids since sliders are shorter

							var sliderAccessWidth = $tp.find('.ui-slider-access:eq(0)').outerWidth(true);

							if (sliderAccessWidth) {

								$tp.find('table:visible').each(function() {

									var $g = $(this),

										oldWidth = $g.outerWidth(),

										oldMarginLeft = $g.css(rtl? 'marginRight':'marginLeft').toString().replace('%', ''),

										newWidth = oldWidth - sliderAccessWidth,

										newMarginLeft = ((oldMarginLeft * newWidth) / oldWidth) + '%',

										css = { width: newWidth, marginRight: 0, marginLeft: 0 };

									css[rtl? 'marginRight':'marginLeft'] = newMarginLeft;

									$g.css(css);

								});

							}

						}

					}, 10);

				}

				// end slideAccess integration



			}

		},



		/*

		* This function tries to limit the ability to go outside the

		* min/max date range

		*/

		_limitMinMaxDateTime: function(dp_inst, adjustSliders) {

			var o = this._defaults,

				dp_date = new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay);



			if (!this._defaults.showTimepicker) {

				return;

			} // No time so nothing to check here



			if ($.datepicker._get(dp_inst, 'minDateTime') !== null && $.datepicker._get(dp_inst, 'minDateTime') !== undefined && dp_date) {

				var minDateTime = $.datepicker._get(dp_inst, 'minDateTime'),

					minDateTimeDate = new Date(minDateTime.getFullYear(), minDateTime.getMonth(), minDateTime.getDate(), 0, 0, 0, 0);



				if (this.hourMinOriginal === null || this.minuteMinOriginal === null || this.secondMinOriginal === null || this.millisecMinOriginal === null) {

					this.hourMinOriginal = o.hourMin;

					this.minuteMinOriginal = o.minuteMin;

					this.secondMinOriginal = o.secondMin;

					this.millisecMinOriginal = o.millisecMin;

				}



				if (dp_inst.settings.timeOnly || minDateTimeDate.getTime() == dp_date.getTime()) {

					this._defaults.hourMin = minDateTime.getHours();

					if (this.hour <= this._defaults.hourMin) {

						this.hour = this._defaults.hourMin;

						this._defaults.minuteMin = minDateTime.getMinutes();

						if (this.minute <= this._defaults.minuteMin) {

							this.minute = this._defaults.minuteMin;

							this._defaults.secondMin = minDateTime.getSeconds();

							if (this.second <= this._defaults.secondMin) {

								this.second = this._defaults.secondMin;

								this._defaults.millisecMin = minDateTime.getMilliseconds();

							} else {

								if (this.millisec < this._defaults.millisecMin) {

									this.millisec = this._defaults.millisecMin;

								}

								this._defaults.millisecMin = this.millisecMinOriginal;

							}

						} else {

							this._defaults.secondMin = this.secondMinOriginal;

							this._defaults.millisecMin = this.millisecMinOriginal;

						}

					} else {

						this._defaults.minuteMin = this.minuteMinOriginal;

						this._defaults.secondMin = this.secondMinOriginal;

						this._defaults.millisecMin = this.millisecMinOriginal;

					}

				} else {

					this._defaults.hourMin = this.hourMinOriginal;

					this._defaults.minuteMin = this.minuteMinOriginal;

					this._defaults.secondMin = this.secondMinOriginal;

					this._defaults.millisecMin = this.millisecMinOriginal;

				}

			}



			if ($.datepicker._get(dp_inst, 'maxDateTime') !== null && $.datepicker._get(dp_inst, 'maxDateTime') !== undefined && dp_date) {

				var maxDateTime = $.datepicker._get(dp_inst, 'maxDateTime'),

					maxDateTimeDate = new Date(maxDateTime.getFullYear(), maxDateTime.getMonth(), maxDateTime.getDate(), 0, 0, 0, 0);



				if (this.hourMaxOriginal === null || this.minuteMaxOriginal === null || this.secondMaxOriginal === null) {

					this.hourMaxOriginal = o.hourMax;

					this.minuteMaxOriginal = o.minuteMax;

					this.secondMaxOriginal = o.secondMax;

					this.millisecMaxOriginal = o.millisecMax;

				}



				if (dp_inst.settings.timeOnly || maxDateTimeDate.getTime() == dp_date.getTime()) {

					this._defaults.hourMax = maxDateTime.getHours();

					if (this.hour >= this._defaults.hourMax) {

						this.hour = this._defaults.hourMax;

						this._defaults.minuteMax = maxDateTime.getMinutes();

						if (this.minute >= this._defaults.minuteMax) {

							this.minute = this._defaults.minuteMax;

							this._defaults.secondMax = maxDateTime.getSeconds();

							if (this.second >= this._defaults.secondMax) {

								this.second = this._defaults.secondMax;

								this._defaults.millisecMax = maxDateTime.getMilliseconds();

							} else {

								if (this.millisec > this._defaults.millisecMax) {

									this.millisec = this._defaults.millisecMax;

								}

								this._defaults.millisecMax = this.millisecMaxOriginal;

							}

						} else {

							this._defaults.secondMax = this.secondMaxOriginal;

							this._defaults.millisecMax = this.millisecMaxOriginal;

						}

					} else {

						this._defaults.minuteMax = this.minuteMaxOriginal;

						this._defaults.secondMax = this.secondMaxOriginal;

						this._defaults.millisecMax = this.millisecMaxOriginal;

					}

				} else {

					this._defaults.hourMax = this.hourMaxOriginal;

					this._defaults.minuteMax = this.minuteMaxOriginal;

					this._defaults.secondMax = this.secondMaxOriginal;

					this._defaults.millisecMax = this.millisecMaxOriginal;

				}

			}



			if (adjustSliders !== undefined && adjustSliders === true) {

				var hourMax = parseInt((this._defaults.hourMax - ((this._defaults.hourMax - this._defaults.hourMin) % this._defaults.stepHour)), 10),

					minMax = parseInt((this._defaults.minuteMax - ((this._defaults.minuteMax - this._defaults.minuteMin) % this._defaults.stepMinute)), 10),

					secMax = parseInt((this._defaults.secondMax - ((this._defaults.secondMax - this._defaults.secondMin) % this._defaults.stepSecond)), 10),

					millisecMax = parseInt((this._defaults.millisecMax - ((this._defaults.millisecMax - this._defaults.millisecMin) % this._defaults.stepMillisec)), 10);



				if (this.hour_slider) {

					this.control.options(this, this.hour_slider, 'hour', { min: this._defaults.hourMin, max: hourMax });

					this.control.value(this, this.hour_slider, 'hour', this.hour - (this.hour % this._defaults.stepHour));

				}

				if (this.minute_slider) {

					this.control.options(this, this.minute_slider, 'minute', { min: this._defaults.minuteMin, max: minMax });

					this.control.value(this, this.minute_slider, 'minute', this.minute - (this.minute % this._defaults.stepMinute));

				}

				if (this.second_slider) {

					this.control.options(this, this.second_slider, 'second', { min: this._defaults.secondMin, max: secMax });

					this.control.value(this, this.second_slider, 'second', this.second - (this.second % this._defaults.stepSecond));

				}

				if (this.millisec_slider) {

					this.control.options(this, this.millisec_slider, 'millisec', { min: this._defaults.millisecMin, max: millisecMax });

					this.control.value(this, this.millisec_slider, 'millisec', this.millisec - (this.millisec % this._defaults.stepMillisec));

				}

			}



		},



		/*

		* when a slider moves, set the internal time...

		* on time change is also called when the time is updated in the text field

		*/

		_onTimeChange: function() {

			var hour = (this.hour_slider) ? this.control.value(this, this.hour_slider, 'hour') : false,

				minute = (this.minute_slider) ? this.control.value(this, this.minute_slider, 'minute') : false,

				second = (this.second_slider) ? this.control.value(this, this.second_slider, 'second') : false,

				millisec = (this.millisec_slider) ? this.control.value(this, this.millisec_slider, 'millisec') : false,

				timezone = (this.timezone_select) ? this.timezone_select.val() : false,

				o = this._defaults,

				pickerTimeFormat = o.pickerTimeFormat || o.timeFormat,

				pickerTimeSuffix = o.pickerTimeSuffix || o.timeSuffix;



			if (typeof(hour) == 'object') {

				hour = false;

			}

			if (typeof(minute) == 'object') {

				minute = false;

			}

			if (typeof(second) == 'object') {

				second = false;

			}

			if (typeof(millisec) == 'object') {

				millisec = false;

			}

			if (typeof(timezone) == 'object') {

				timezone = false;

			}



			if (hour !== false) {

				hour = parseInt(hour, 10);

			}

			if (minute !== false) {

				minute = parseInt(minute, 10);

			}

			if (second !== false) {

				second = parseInt(second, 10);

			}

			if (millisec !== false) {

				millisec = parseInt(millisec, 10);

			}



			var ampm = o[hour < 12 ? 'amNames' : 'pmNames'][0];



			// If the update was done in the input field, the input field should not be updated.

			// If the update was done using the sliders, update the input field.

			var hasChanged = (hour != this.hour || minute != this.minute || second != this.second || millisec != this.millisec 

								|| (this.ampm.length > 0 && (hour < 12) != ($.inArray(this.ampm.toUpperCase(), this.amNames) !== -1)) 

								|| ((this.timezone === null && timezone != this.defaultTimezone) || (this.timezone !== null && timezone != this.timezone)));



			if (hasChanged) {



				if (hour !== false) {

					this.hour = hour;

				}

				if (minute !== false) {

					this.minute = minute;

				}

				if (second !== false) {

					this.second = second;

				}

				if (millisec !== false) {

					this.millisec = millisec;

				}

				if (timezone !== false) {

					this.timezone = timezone;

				}



				if (!this.inst) {

					this.inst = $.datepicker._getInst(this.$input[0]);

				}



				this._limitMinMaxDateTime(this.inst, true);

			}

			if (useAmpm(o.timeFormat)) {

				this.ampm = ampm;

			}



			// Updates the time within the timepicker

			this.formattedTime = $.datepicker.formatTime(o.timeFormat, this, o);

			if (this.$timeObj) {

				if(pickerTimeFormat === o.timeFormat){

					this.$timeObj.text(this.formattedTime + pickerTimeSuffix);

				}

				else{

					this.$timeObj.text($.datepicker.formatTime(pickerTimeFormat, this, o) + pickerTimeSuffix);

				}

			}



			this.timeDefined = true;

			if (hasChanged) {

				this._updateDateTime();

			}

		},



		/*

		* call custom onSelect.

		* bind to sliders slidestop, and grid click.

		*/

		_onSelectHandler: function() {

			var onSelect = this._defaults.onSelect || this.inst.settings.onSelect;

			var inputEl = this.$input ? this.$input[0] : null;

			if (onSelect && inputEl) {

				onSelect.apply(inputEl, [this.formattedDateTime, this]);

			}

		},



		/*

		* update our input with the new date time..

		*/

		_updateDateTime: function(dp_inst) {

			dp_inst = this.inst || dp_inst;

			var dt = $.datepicker._daylightSavingAdjust(new Date(dp_inst.selectedYear, dp_inst.selectedMonth, dp_inst.selectedDay)),

				dateFmt = $.datepicker._get(dp_inst, 'dateFormat'),

				formatCfg = $.datepicker._getFormatConfig(dp_inst),

				timeAvailable = dt !== null && this.timeDefined;

			this.formattedDate = $.datepicker.formatDate(dateFmt, (dt === null ? new Date() : dt), formatCfg);

			var formattedDateTime = this.formattedDate;



			/*

			* remove following lines to force every changes in date picker to change the input value

			* Bug descriptions: when an input field has a default value, and click on the field to pop up the date picker. 

			* If the user manually empty the value in the input field, the date picker will never change selected value.

			*/

			//if (dp_inst.lastVal !== undefined && (dp_inst.lastVal.length > 0 && this.$input.val().length === 0)) {

			//	return;

			//}



			if (this._defaults.timeOnly === true) {

				formattedDateTime = this.formattedTime;

			} else if (this._defaults.timeOnly !== true && (this._defaults.alwaysSetTime || timeAvailable)) {

				formattedDateTime += this._defaults.separator + this.formattedTime + this._defaults.timeSuffix;

			}



			this.formattedDateTime = formattedDateTime;



			if (!this._defaults.showTimepicker) {

				this.$input.val(this.formattedDate);

			} else if (this.$altInput && this._defaults.altFieldTimeOnly === true) {

				this.$altInput.val(this.formattedTime);

				this.$input.val(this.formattedDate);

			} else if (this.$altInput) {

				this.$input.val(formattedDateTime);

				var altFormattedDateTime = '',

					altSeparator = this._defaults.altSeparator ? this._defaults.altSeparator : this._defaults.separator,

					altTimeSuffix = this._defaults.altTimeSuffix ? this._defaults.altTimeSuffix : this._defaults.timeSuffix;



				if (this._defaults.altFormat) altFormattedDateTime = $.datepicker.formatDate(this._defaults.altFormat, (dt === null ? new Date() : dt), formatCfg);

				else altFormattedDateTime = this.formattedDate;

				if (altFormattedDateTime) altFormattedDateTime += altSeparator;

				if (this._defaults.altTimeFormat) altFormattedDateTime += $.datepicker.formatTime(this._defaults.altTimeFormat, this, this._defaults) + altTimeSuffix;

				else altFormattedDateTime += this.formattedTime + altTimeSuffix;

				this.$altInput.val(altFormattedDateTime);

			} else {

				this.$input.val(formattedDateTime);

			}



			this.$input.trigger("change");

		},



		_onFocus: function() {

			if (!this.$input.val() && this._defaults.defaultValue) {

				this.$input.val(this._defaults.defaultValue);

				var inst = $.datepicker._getInst(this.$input.get(0)),

					tp_inst = $.datepicker._get(inst, 'timepicker');

				if (tp_inst) {

					if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {

						try {

							$.datepicker._updateDatepicker(inst);

						} catch (err) {

							$.datepicker.log(err);

						}

					}

				}

			}

		},



		/*

		* Small abstraction to control types

		* We can add more, just be sure to follow the pattern: create, options, value

		*/

		_controls: {

			// slider methods

			slider: {

				create: function(tp_inst, obj, unit, val, min, max, step){

					var rtl = tp_inst._defaults.isRTL; // if rtl go -60->0 instead of 0->60

					return obj.prop('slide', null).slider({

						orientation: "horizontal",

						value: rtl? val*-1 : val,

						min: rtl? max*-1 : min,

						max: rtl? min*-1 : max,

						step: step,

						slide: function(event, ui) {

							tp_inst.control.value(tp_inst, $(this), unit, rtl? ui.value*-1:ui.value);

							tp_inst._onTimeChange();

						},

						stop: function(event, ui) {

							tp_inst._onSelectHandler();

						}

					});	

				},

				options: function(tp_inst, obj, unit, opts, val){

					if(tp_inst._defaults.isRTL){

						if(typeof(opts) == 'string'){

							if(opts == 'min' || opts == 'max'){

								if(val !== undefined)

									return obj.slider(opts, val*-1);

								return Math.abs(obj.slider(opts));

							}

							return obj.slider(opts);

						}

						var min = opts.min, 

							max = opts.max;

						opts.min = opts.max = null;

						if(min !== undefined)

							opts.max = min * -1;

						if(max !== undefined)

							opts.min = max * -1;

						return obj.slider(opts);

					}

					if(typeof(opts) == 'string' && val !== undefined)

							return obj.slider(opts, val);

					return obj.slider(opts);

				},

				value: function(tp_inst, obj, unit, val){

					if(tp_inst._defaults.isRTL){

						if(val !== undefined)

							return obj.slider('value', val*-1);

						return Math.abs(obj.slider('value'));

					}

					if(val !== undefined)

						return obj.slider('value', val);

					return obj.slider('value');

				}

			},

			// select methods

			select: {

				create: function(tp_inst, obj, unit, val, min, max, step){

					var sel = '<select class="ui-timepicker-select" data-unit="'+ unit +'" data-min="'+ min +'" data-max="'+ max +'" data-step="'+ step +'">',

						ul = tp_inst._defaults.timeFormat.indexOf('t') !== -1? 'toLowerCase':'toUpperCase',

						m = 0;



					for(var i=min; i<=max; i+=step){						

						sel += '<option value="'+ i +'"'+ (i==val? ' selected':'') +'>';

						if(unit == 'hour' && useAmpm(tp_inst._defaults.pickerTimeFormat || tp_inst._defaults.timeFormat))

							sel += $.datepicker.formatTime("hh TT", {hour:i}, tp_inst._defaults);

						else if(unit == 'millisec' || i >= 10) sel += i;

						else sel += '0'+ i.toString();

						sel += '</option>';

					}

					sel += '</select>';



					obj.children('select').remove();



					$(sel).appendTo(obj).change(function(e){

						tp_inst._onTimeChange();

						tp_inst._onSelectHandler();

					});



					return obj;

				},

				options: function(tp_inst, obj, unit, opts, val){

					var o = {},

						$t = obj.children('select');

					if(typeof(opts) == 'string'){

						if(val === undefined)

							return $t.data(opts);

						o[opts] = val;	

					}

					else o = opts;

					return tp_inst.control.create(tp_inst, obj, $t.data('unit'), $t.val(), o.min || $t.data('min'), o.max || $t.data('max'), o.step || $t.data('step'));

				},

				value: function(tp_inst, obj, unit, val){

					var $t = obj.children('select');

					if(val !== undefined)

						return $t.val(val);

					return $t.val();

				}

			}

		} // end _controls



	});



	$.fn.extend({

		/*

		* shorthand just to use timepicker..

		*/

		timepicker: function(o) {

			o = o || {};

			var tmp_args = Array.prototype.slice.call(arguments);



			if (typeof o == 'object') {

				tmp_args[0] = $.extend(o, {

					timeOnly: true

				});

			}



			return $(this).each(function() {

				$.fn.datetimepicker.apply($(this), tmp_args);

			});

		},



		/*

		* extend timepicker to datepicker

		*/

		datetimepicker: function(o) {

			o = o || {};

			var tmp_args = arguments;



			if (typeof(o) == 'string') {

				if (o == 'getDate') {

					return $.fn.datepicker.apply($(this[0]), tmp_args);

				} else {

					return this.each(function() {

						var $t = $(this);

						$t.datepicker.apply($t, tmp_args);

					});

				}

			} else {

				return this.each(function() {

					var $t = $(this);

					$t.datepicker($.timepicker._newInst($t, o)._defaults);

				});

			}

		}

	});



	/*

	* Public Utility to parse date and time

	*/

	$.datepicker.parseDateTime = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {

		var parseRes = parseDateTimeInternal(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings);

		if (parseRes.timeObj) {

			var t = parseRes.timeObj;

			parseRes.date.setHours(t.hour, t.minute, t.second, t.millisec);

		}



		return parseRes.date;

	};



	/*

	* Public utility to parse time

	*/

	$.datepicker.parseTime = function(timeFormat, timeString, options) {		

		var o = extendRemove(extendRemove({}, $.timepicker._defaults), options || {});



		// Strict parse requires the timeString to match the timeFormat exactly

		var strictParse = function(f, s, o){



			// pattern for standard and localized AM/PM markers

			var getPatternAmpm = function(amNames, pmNames) {

				var markers = [];

				if (amNames) {

					$.merge(markers, amNames);

				}

				if (pmNames) {

					$.merge(markers, pmNames);

				}

				markers = $.map(markers, function(val) {

					return val.replace(/[.*+?|()\[\]{}\\]/g, '\\$&');

				});

				return '(' + markers.join('|') + ')?';

			};



			// figure out position of time elements.. cause js cant do named captures

			var getFormatPositions = function(timeFormat) {

				var finds = timeFormat.toLowerCase().match(/(h{1,2}|m{1,2}|s{1,2}|l{1}|t{1,2}|z|'.*?')/g),

					orders = {

						h: -1,

						m: -1,

						s: -1,

						l: -1,

						t: -1,

						z: -1

					};



				if (finds) {

					for (var i = 0; i < finds.length; i++) {

						if (orders[finds[i].toString().charAt(0)] == -1) {

							orders[finds[i].toString().charAt(0)] = i + 1;

						}

					}

				}

				return orders;

			};



			var regstr = '^' + f.toString()

					.replace(/([hH]{1,2}|mm?|ss?|[tT]{1,2}|[lz]|'.*?')/g, function (match) {

							var ml = match.length;

							switch (match.charAt(0).toLowerCase()) {

								case 'h': return ml === 1? '(\\d?\\d)':'(\\d{'+ml+'})';

								case 'm': return ml === 1? '(\\d?\\d)':'(\\d{'+ml+'})';

								case 's': return ml === 1? '(\\d?\\d)':'(\\d{'+ml+'})';

								case 'l': return '(\\d?\\d?\\d)';

								case 'z': return '(z|[-+]\\d\\d:?\\d\\d|\\S+)?';

								case 't': return getPatternAmpm(o.amNames, o.pmNames);

								default:    // literal escaped in quotes

									return '(' + match.replace(/\'/g, "").replace(/(\.|\$|\^|\\|\/|\(|\)|\[|\]|\?|\+|\*)/g, function (m) { return "\\" + m; }) + ')?';

							}

						})

					.replace(/\s/g, '\\s?') +

					o.timeSuffix + '$',

				order = getFormatPositions(f),

				ampm = '',

				treg;



			treg = s.match(new RegExp(regstr, 'i'));



			var resTime = {

				hour: 0,

				minute: 0,

				second: 0,

				millisec: 0

			};



			if (treg) {

				if (order.t !== -1) {

					if (treg[order.t] === undefined || treg[order.t].length === 0) {

						ampm = '';

						resTime.ampm = '';

					} else {

						ampm = $.inArray(treg[order.t].toUpperCase(), o.amNames) !== -1 ? 'AM' : 'PM';

						resTime.ampm = o[ampm == 'AM' ? 'amNames' : 'pmNames'][0];

					}

				}



				if (order.h !== -1) {

					if (ampm == 'AM' && treg[order.h] == '12') {

						resTime.hour = 0; // 12am = 0 hour

					} else {

						if (ampm == 'PM' && treg[order.h] != '12') {

							resTime.hour = parseInt(treg[order.h], 10) + 12; // 12pm = 12 hour, any other pm = hour + 12

						} else {

							resTime.hour = Number(treg[order.h]);

						}

					}

				}



				if (order.m !== -1) {

					resTime.minute = Number(treg[order.m]);

				}

				if (order.s !== -1) {

					resTime.second = Number(treg[order.s]);

				}

				if (order.l !== -1) {

					resTime.millisec = Number(treg[order.l]);

				}

				if (order.z !== -1 && treg[order.z] !== undefined) {

					var tz = treg[order.z].toUpperCase();

					switch (tz.length) {

					case 1:

						// Z

						tz = o.timezoneIso8601 ? 'Z' : '+0000';

						break;

					case 5:

						// +hhmm

						if (o.timezoneIso8601) {

							tz = tz.substring(1) == '0000' ? 'Z' : tz.substring(0, 3) + ':' + tz.substring(3);

						}

						break;

					case 6:

						// +hh:mm

						if (!o.timezoneIso8601) {

							tz = tz == 'Z' || tz.substring(1) == '00:00' ? '+0000' : tz.replace(/:/, '');

						} else {

							if (tz.substring(1) == '00:00') {

								tz = 'Z';

							}

						}

						break;

					}

					resTime.timezone = tz;

				}





				return resTime;

			}

			return false;

		};// end strictParse



		// First try JS Date, if that fails, use strictParse

		var looseParse = function(f,s,o){

			try{

				var d = new Date('2012-01-01 '+ s);

				if(isNaN(d.getTime())){

					d = new Date('2012-01-01T'+ s);

					if(isNaN(d.getTime())){

						d = new Date('01/01/2012 '+ s);

						if(isNaN(d.getTime())){

							throw "Unable to parse time with native Date: "+ s;

						}

					}

				}



				return {

					hour: d.getHours(),

					minute: d.getMinutes(),

					second: d.getSeconds(),

					millisec: d.getMilliseconds(),

					timezone: $.timepicker.timeZoneOffsetString(d)

				};

			}

			catch(err){

				try{

					return strictParse(f,s,o);

				}

				catch(err2){

					$.datepicker.log("Unable to parse \ntimeString: "+ s +"\ntimeFormat: "+ f);

				}				

			}

			return false;

		}; // end looseParse

		

		if(typeof o.parse === "function"){

			return o.parse(timeFormat, timeString, o)

		}

		if(o.parse === 'loose'){

			return looseParse(timeFormat, timeString, o);

		}

		return strictParse(timeFormat, timeString, o);

	};



	/*

	* Public utility to format the time

	* format = string format of the time

	* time = a {}, not a Date() for timezones

	* options = essentially the regional[].. amNames, pmNames, ampm

	*/

	$.datepicker.formatTime = function(format, time, options) {

		options = options || {};

		options = $.extend({}, $.timepicker._defaults, options);

		time = $.extend({

			hour: 0,

			minute: 0,

			second: 0,

			millisec: 0,

			timezone: '+0000'

		}, time);



		var tmptime = format,

			ampmName = options.amNames[0],

			hour = parseInt(time.hour, 10);



		if (hour > 11) {

			ampmName = options.pmNames[0];

		}



		tmptime = tmptime.replace(/(?:HH?|hh?|mm?|ss?|[tT]{1,2}|[lz]|('.*?'|".*?"))/g, function(match) {

		switch (match) {

			case 'HH':

				return ('0' + hour).slice(-2);

			case 'H':

				return hour;

			case 'hh':

				return ('0' + convert24to12(hour)).slice(-2);

			case 'h':

				return convert24to12(hour);

			case 'mm':

				return ('0' + time.minute).slice(-2);

			case 'm':

				return time.minute;

			case 'ss':

				return ('0' + time.second).slice(-2);

			case 's':

				return time.second;

			case 'l':

				return ('00' + time.millisec).slice(-3);

			case 'z':

				return time.timezone === null? options.defaultTimezone : time.timezone;

			case 'T': 

				return ampmName.charAt(0).toUpperCase();

			case 'TT': 

				return ampmName.toUpperCase();

			case 't':

				return ampmName.charAt(0).toLowerCase();

			case 'tt':

				return ampmName.toLowerCase();

			default:

				return match.replace(/\'/g, "") || "'";

			}

		});



		tmptime = $.trim(tmptime);

		return tmptime;

	};



	/*

	* the bad hack :/ override datepicker so it doesnt close on select

	// inspired: http://stackoverflow.com/questions/1252512/jquery-datepicker-prevent-closing-picker-when-clicking-a-date/1762378#1762378

	*/

	$.datepicker._base_selectDate = $.datepicker._selectDate;

	$.datepicker._selectDate = function(id, dateStr) {

		var inst = this._getInst($(id)[0]),

			tp_inst = this._get(inst, 'timepicker');



		if (tp_inst) {

			tp_inst._limitMinMaxDateTime(inst, true);

			inst.inline = inst.stay_open = true;

			//This way the onSelect handler called from calendarpicker get the full dateTime

			this._base_selectDate(id, dateStr);

			inst.inline = inst.stay_open = false;

			this._notifyChange(inst);

			this._updateDatepicker(inst);

		} else {

			this._base_selectDate(id, dateStr);

		}

	};



	/*

	* second bad hack :/ override datepicker so it triggers an event when changing the input field

	* and does not redraw the datepicker on every selectDate event

	*/

	$.datepicker._base_updateDatepicker = $.datepicker._updateDatepicker;

	$.datepicker._updateDatepicker = function(inst) {



		// don't popup the datepicker if there is another instance already opened

		var input = inst.input[0];

		if ($.datepicker._curInst && $.datepicker._curInst != inst && $.datepicker._datepickerShowing && $.datepicker._lastInput != input) {

			return;

		}



		if (typeof(inst.stay_open) !== 'boolean' || inst.stay_open === false) {



			this._base_updateDatepicker(inst);



			// Reload the time control when changing something in the input text field.

			var tp_inst = this._get(inst, 'timepicker');

			if (tp_inst) {

				tp_inst._addTimePicker(inst);



//				if (tp_inst._defaults.useLocalTimezone) { //checks daylight saving with the new date.

//					var date = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay, 12);

//					selectLocalTimeZone(tp_inst, date);

//					tp_inst._onTimeChange();

//				}

			}

		}

	};



	/*

	* third bad hack :/ override datepicker so it allows spaces and colon in the input field

	*/

	$.datepicker._base_doKeyPress = $.datepicker._doKeyPress;

	$.datepicker._doKeyPress = function(event) {

		var inst = $.datepicker._getInst(event.target),

			tp_inst = $.datepicker._get(inst, 'timepicker');



		if (tp_inst) {

			if ($.datepicker._get(inst, 'constrainInput')) {

				var ampm = useAmpm(tp_inst._defaults.timeFormat),

					dateChars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat')),

					datetimeChars = tp_inst._defaults.timeFormat.toString()

											.replace(/[hms]/g, '')

											.replace(/TT/g, ampm ? 'APM' : '')

											.replace(/Tt/g, ampm ? 'AaPpMm' : '')

											.replace(/tT/g, ampm ? 'AaPpMm' : '')

											.replace(/T/g, ampm ? 'AP' : '')

											.replace(/tt/g, ampm ? 'apm' : '')

											.replace(/t/g, ampm ? 'ap' : '') + 

											" " + tp_inst._defaults.separator + 

											tp_inst._defaults.timeSuffix + 

											(tp_inst._defaults.showTimezone ? tp_inst._defaults.timezoneList.join('') : '') + 

											(tp_inst._defaults.amNames.join('')) + (tp_inst._defaults.pmNames.join('')) + 

											dateChars,

					chr = String.fromCharCode(event.charCode === undefined ? event.keyCode : event.charCode);

				return event.ctrlKey || (chr < ' ' || !dateChars || datetimeChars.indexOf(chr) > -1);

			}

		}



		return $.datepicker._base_doKeyPress(event);

	};



	/*

	* Fourth bad hack :/ override _updateAlternate function used in inline mode to init altField

	*/

	$.datepicker._base_updateAlternate = $.datepicker._updateAlternate;

	/* Update any alternate field to synchronise with the main field. */

	$.datepicker._updateAlternate = function(inst) {

		var tp_inst = this._get(inst, 'timepicker');

		if(tp_inst){

			var altField = tp_inst._defaults.altField;

			if (altField) { // update alternate field too

				var altFormat = tp_inst._defaults.altFormat || tp_inst._defaults.dateFormat,

					date = this._getDate(inst),

					formatCfg = $.datepicker._getFormatConfig(inst),

					altFormattedDateTime = '', 

					altSeparator = tp_inst._defaults.altSeparator ? tp_inst._defaults.altSeparator : tp_inst._defaults.separator, 

					altTimeSuffix = tp_inst._defaults.altTimeSuffix ? tp_inst._defaults.altTimeSuffix : tp_inst._defaults.timeSuffix,

					altTimeFormat = tp_inst._defaults.altTimeFormat !== null ? tp_inst._defaults.altTimeFormat : tp_inst._defaults.timeFormat;

				

				altFormattedDateTime += $.datepicker.formatTime(altTimeFormat, tp_inst, tp_inst._defaults) + altTimeSuffix;

				if(!tp_inst._defaults.timeOnly && !tp_inst._defaults.altFieldTimeOnly && date !== null){

					if(tp_inst._defaults.altFormat)

						altFormattedDateTime = $.datepicker.formatDate(tp_inst._defaults.altFormat, date, formatCfg) + altSeparator + altFormattedDateTime;

					else altFormattedDateTime = tp_inst.formattedDate + altSeparator + altFormattedDateTime;

				}

				$(altField).val(altFormattedDateTime);

			}

		}

		else{

			$.datepicker._base_updateAlternate(inst);

		}

	};



	/*

	* Override key up event to sync manual input changes.

	*/

	$.datepicker._base_doKeyUp = $.datepicker._doKeyUp;

	$.datepicker._doKeyUp = function(event) {

		var inst = $.datepicker._getInst(event.target),

			tp_inst = $.datepicker._get(inst, 'timepicker');



		if (tp_inst) {

			if (tp_inst._defaults.timeOnly && (inst.input.val() != inst.lastVal)) {

				try {

					$.datepicker._updateDatepicker(inst);

				} catch (err) {

					$.datepicker.log(err);

				}

			}

		}



		return $.datepicker._base_doKeyUp(event);

	};



	/*

	* override "Today" button to also grab the time.

	*/

	$.datepicker._base_gotoToday = $.datepicker._gotoToday;

	$.datepicker._gotoToday = function(id) {

		var inst = this._getInst($(id)[0]),

			$dp = inst.dpDiv;

		this._base_gotoToday(id);

		var tp_inst = this._get(inst, 'timepicker');

		selectLocalTimeZone(tp_inst);

		var now = new Date();

		this._setTime(inst, now);

		$('.ui-datepicker-today', $dp).click();

	};



	/*

	* Disable & enable the Time in the datetimepicker

	*/

	$.datepicker._disableTimepickerDatepicker = function(target) {

		var inst = this._getInst(target);

		if (!inst) {

			return;

		}



		var tp_inst = this._get(inst, 'timepicker');

		$(target).datepicker('getDate'); // Init selected[Year|Month|Day]

		if (tp_inst) {

			tp_inst._defaults.showTimepicker = false;

			tp_inst._updateDateTime(inst);

		}

	};



	$.datepicker._enableTimepickerDatepicker = function(target) {

		var inst = this._getInst(target);

		if (!inst) {

			return;

		}



		var tp_inst = this._get(inst, 'timepicker');

		$(target).datepicker('getDate'); // Init selected[Year|Month|Day]

		if (tp_inst) {

			tp_inst._defaults.showTimepicker = true;

			tp_inst._addTimePicker(inst); // Could be disabled on page load

			tp_inst._updateDateTime(inst);

		}

	};



	/*

	* Create our own set time function

	*/

	$.datepicker._setTime = function(inst, date) {

		var tp_inst = this._get(inst, 'timepicker');

		if (tp_inst) {

			var defaults = tp_inst._defaults;



			// calling _setTime with no date sets time to defaults

			tp_inst.hour = date ? date.getHours() : defaults.hour;

			tp_inst.minute = date ? date.getMinutes() : defaults.minute;

			tp_inst.second = date ? date.getSeconds() : defaults.second;

			tp_inst.millisec = date ? date.getMilliseconds() : defaults.millisec;



			//check if within min/max times.. 

			tp_inst._limitMinMaxDateTime(inst, true);



			tp_inst._onTimeChange();

			tp_inst._updateDateTime(inst);

		}

	};



	/*

	* Create new public method to set only time, callable as $().datepicker('setTime', date)

	*/

	$.datepicker._setTimeDatepicker = function(target, date, withDate) {

		var inst = this._getInst(target);

		if (!inst) {

			return;

		}



		var tp_inst = this._get(inst, 'timepicker');



		if (tp_inst) {

			this._setDateFromField(inst);

			var tp_date;

			if (date) {

				if (typeof date == "string") {

					tp_inst._parseTime(date, withDate);

					tp_date = new Date();

					tp_date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);

				} else {

					tp_date = new Date(date.getTime());

				}

				if (tp_date.toString() == 'Invalid Date') {

					tp_date = undefined;

				}

				this._setTime(inst, tp_date);

			}

		}



	};



	/*

	* override setDate() to allow setting time too within Date object

	*/

	$.datepicker._base_setDateDatepicker = $.datepicker._setDateDatepicker;

	$.datepicker._setDateDatepicker = function(target, date) {

		var inst = this._getInst(target);

		if (!inst) {

			return;

		}



		var tp_date = (date instanceof Date) ? new Date(date.getTime()) : date;



		this._updateDatepicker(inst);

		this._base_setDateDatepicker.apply(this, arguments);

		this._setTimeDatepicker(target, tp_date, true);

	};



	/*

	* override getDate() to allow getting time too within Date object

	*/

	$.datepicker._base_getDateDatepicker = $.datepicker._getDateDatepicker;

	$.datepicker._getDateDatepicker = function(target, noDefault) {

		var inst = this._getInst(target);

		if (!inst) {

			return;

		}



		var tp_inst = this._get(inst, 'timepicker');



		if (tp_inst) {

			// if it hasn't yet been defined, grab from field

			if(inst.lastVal === undefined){

				this._setDateFromField(inst, noDefault);

			}



			var date = this._getDate(inst);

			if (date && tp_inst._parseTime($(target).val(), tp_inst.timeOnly)) {

				date.setHours(tp_inst.hour, tp_inst.minute, tp_inst.second, tp_inst.millisec);

			}

			return date;

		}

		return this._base_getDateDatepicker(target, noDefault);

	};



	/*

	* override parseDate() because UI 1.8.14 throws an error about "Extra characters"

	* An option in datapicker to ignore extra format characters would be nicer.

	*/

	$.datepicker._base_parseDate = $.datepicker.parseDate;

	$.datepicker.parseDate = function(format, value, settings) {

		var date;

		try {

			date = this._base_parseDate(format, value, settings);

		} catch (err) {

			// Hack!  The error message ends with a colon, a space, and

			// the "extra" characters.  We rely on that instead of

			// attempting to perfectly reproduce the parsing algorithm.

			date = this._base_parseDate(format, value.substring(0,value.length-(err.length-err.indexOf(':')-2)), settings);

			$.datepicker.log("Error parsing the date string: " + err + "\ndate string = " + value + "\ndate format = " + format);

		}

		return date;

	};



	/*

	* override formatDate to set date with time to the input

	*/

	$.datepicker._base_formatDate = $.datepicker._formatDate;

	$.datepicker._formatDate = function(inst, day, month, year) {

		var tp_inst = this._get(inst, 'timepicker');

		if (tp_inst) {

			tp_inst._updateDateTime(inst);

			return tp_inst.$input.val();

		}

		return this._base_formatDate(inst);

	};



	/*

	* override options setter to add time to maxDate(Time) and minDate(Time). MaxDate

	*/

	$.datepicker._base_optionDatepicker = $.datepicker._optionDatepicker;

	$.datepicker._optionDatepicker = function(target, name, value) {

		var inst = this._getInst(target),

	        name_clone;

		if (!inst) {

			return null;

		}



		var tp_inst = this._get(inst, 'timepicker');

		if (tp_inst) {

			var min = null,

				max = null,

				onselect = null,

				overrides = tp_inst._defaults.evnts,

				fns = {},

				prop;

		    if (typeof name == 'string') { // if min/max was set with the string

		        if (name === 'minDate' || name === 'minDateTime') {

		            min = value;

		        } else if (name === 'maxDate' || name === 'maxDateTime') {

		            max = value;

		        } else if (name === 'onSelect') {

		            onselect = value;

		        } else if (overrides.hasOwnProperty(name)) {

		            if (typeof (value) === 'undefined') {

		                return overrides[name];

		            }

		            fns[name] = value;

		            name_clone = {}; //empty results in exiting function after overrides updated

		        }

		    } else if (typeof name == 'object') { //if min/max was set with the JSON

		        if (name.minDate) {

		            min = name.minDate;

		        } else if (name.minDateTime) {

		            min = name.minDateTime;

		        } else if (name.maxDate) {

		            max = name.maxDate;

		        } else if (name.maxDateTime) {

		            max = name.maxDateTime;

		        }

		        for (prop in overrides) {

		            if (overrides.hasOwnProperty(prop) && name[prop]) {

		                fns[prop] = name[prop];

		            }

		        }

		    }

		    for (prop in fns) {

		        if (fns.hasOwnProperty(prop)) {

		            overrides[prop] = fns[prop];

		            if (!name_clone) { name_clone = $.extend({}, name);}

		            delete name_clone[prop];

		        }

		    }

		    if (name_clone && isEmptyObject(name_clone)) { return; }

		    if (min) { //if min was set

		        if (min === 0) {

		            min = new Date();

		        } else {

		            min = new Date(min);

		        }

		        tp_inst._defaults.minDate = min;

		        tp_inst._defaults.minDateTime = min;

		    } else if (max) { //if max was set

		        if (max === 0) {

		            max = new Date();

		        } else {

		            max = new Date(max);

		        }

		        tp_inst._defaults.maxDate = max;

		        tp_inst._defaults.maxDateTime = max;

		    } else if (onselect) {

		        tp_inst._defaults.onSelect = onselect;

		    }

		}

		if (value === undefined) {

			return this._base_optionDatepicker.call($.datepicker, target, name);

		}

		return this._base_optionDatepicker.call($.datepicker, target, name_clone || name, value);

	};

	/*

	* jQuery isEmptyObject does not check hasOwnProperty - if someone has added to the object prototype,

	* it will return false for all objects

	*/

	var isEmptyObject = function(obj) {

		var prop;

		for (prop in obj) {

			if (obj.hasOwnProperty(obj)) {

				return false;

			}

		}

		return true;

	};



	/*

	* jQuery extend now ignores nulls!

	*/

	var extendRemove = function(target, props) {

		$.extend(target, props);

		for (var name in props) {

			if (props[name] === null || props[name] === undefined) {

				target[name] = props[name];

			}

		}

		return target;

	};



	/*

	* Determine by the time format if should use ampm

	* Returns true if should use ampm, false if not

	*/

	var useAmpm = function(timeFormat){

		return (timeFormat.indexOf('t') !== -1 && timeFormat.indexOf('h') !== -1);

	};



	/*

	* Converts 24 hour format into 12 hour

	* Returns 12 hour without leading 0

	*/

	var convert24to12 = function(hour) {

		if (hour > 12) {

			hour = hour - 12;

		}



		if (hour == 0) {

			hour = 12;

		}



		return String(hour);

	};



	/*

	* Splits datetime string into date ans time substrings.

	* Throws exception when date can't be parsed

	* Returns [dateString, timeString]

	*/

	var splitDateTime = function(dateFormat, dateTimeString, dateSettings, timeSettings) {

		try {

			// The idea is to get the number separator occurances in datetime and the time format requested (since time has 

			// fewer unknowns, mostly numbers and am/pm). We will use the time pattern to split.

			var separator = timeSettings && timeSettings.separator ? timeSettings.separator : $.timepicker._defaults.separator,

				format = timeSettings && timeSettings.timeFormat ? timeSettings.timeFormat : $.timepicker._defaults.timeFormat,

				timeParts = format.split(separator), // how many occurances of separator may be in our format?

				timePartsLen = timeParts.length,

				allParts = dateTimeString.split(separator),

				allPartsLen = allParts.length;



			if (allPartsLen > 1) {

				return [

						allParts.splice(0,allPartsLen-timePartsLen).join(separator),

						allParts.splice(0,timePartsLen).join(separator)

					];

			}



		} catch (err) {

			$.datepicker.log('Could not split the date from the time. Please check the following datetimepicker options' +

					"\nthrown error: " + err +

					"\ndateTimeString" + dateTimeString +

					"\ndateFormat = " + dateFormat +

					"\nseparator = " + timeSettings.separator +

					"\ntimeFormat = " + timeSettings.timeFormat);



			if (err.indexOf(":") >= 0) {

				// Hack!  The error message ends with a colon, a space, and

				// the "extra" characters.  We rely on that instead of

				// attempting to perfectly reproduce the parsing algorithm.

				var dateStringLength = dateTimeString.length - (err.length - err.indexOf(':') - 2),

					timeString = dateTimeString.substring(dateStringLength);



				return [$.trim(dateTimeString.substring(0, dateStringLength)), $.trim(dateTimeString.substring(dateStringLength))];



			} else {

				throw err;

			}

		}

		return [dateTimeString, ''];

	};



	/*

	* Internal function to parse datetime interval

	* Returns: {date: Date, timeObj: Object}, where

	*   date - parsed date without time (type Date)

	*   timeObj = {hour: , minute: , second: , millisec: } - parsed time. Optional

	*/

	var parseDateTimeInternal = function(dateFormat, timeFormat, dateTimeString, dateSettings, timeSettings) {

		var date;

		var splitRes = splitDateTime(dateFormat, dateTimeString, dateSettings, timeSettings);

		date = $.datepicker._base_parseDate(dateFormat, splitRes[0], dateSettings);

		if (splitRes[1] !== '') {

			var timeString = splitRes[1],

				parsedTime = $.datepicker.parseTime(timeFormat, timeString, timeSettings);



			if (parsedTime === null) {

				throw 'Wrong time format';

			}

			return {

				date: date,

				timeObj: parsedTime

			};

		} else {

			return {

				date: date

			};

		}

	};



	/*

	* Internal function to set timezone_select to the local timezone

	*/

	var selectLocalTimeZone = function(tp_inst, date) {

		if (tp_inst && tp_inst.timezone_select) {

			tp_inst._defaults.useLocalTimezone = true;

			var now = typeof date !== 'undefined' ? date : new Date();

			var tzoffset = $.timepicker.timeZoneOffsetString(now);

			if (tp_inst._defaults.timezoneIso8601) {

				tzoffset = tzoffset.substring(0, 3) + ':' + tzoffset.substring(3);

			}

			tp_inst.timezone_select.val(tzoffset);

		}

	};



	/*

	* Create a Singleton Insance

	*/

	$.timepicker = new Timepicker();



	/**

	 * Get the timezone offset as string from a date object (eg '+0530' for UTC+5.5)

	 * @param  date

	 * @return string

	 */

	$.timepicker.timeZoneOffsetString = function(date) {

		var off = date.getTimezoneOffset() * -1,

			minutes = off % 60,

			hours = (off - minutes) / 60;

		return (off >= 0 ? '+' : '-') + ('0' + (hours * 101).toString()).slice(-2) + ('0' + (minutes * 101).toString()).slice(-2);

	};



	/**

	 * Calls `timepicker()` on the `startTime` and `endTime` elements, and configures them to

	 * enforce date range limits.

	 * n.b. The input value must be correctly formatted (reformatting is not supported)

	 * @param  Element startTime

	 * @param  Element endTime

	 * @param  obj options Options for the timepicker() call

	 * @return jQuery

	 */

	$.timepicker.timeRange = function(startTime, endTime, options) {

		return $.timepicker.handleRange('timepicker', startTime, endTime, options);

	};



	/**

	 * Calls `datetimepicker` on the `startTime` and `endTime` elements, and configures them to

	 * enforce date range limits.

	 * @param  Element startTime

	 * @param  Element endTime

	 * @param  obj options Options for the `timepicker()` call. Also supports `reformat`,

	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.

	 * @param  string method Can be used to specify the type of picker to be added

	 * @return jQuery

	 */

	$.timepicker.dateTimeRange = function(startTime, endTime, options) {

		$.timepicker.dateRange(startTime, endTime, options, 'datetimepicker');

	};



	/**

	 * Calls `method` on the `startTime` and `endTime` elements, and configures them to

	 * enforce date range limits.

	 * @param  Element startTime

	 * @param  Element endTime

	 * @param  obj options Options for the `timepicker()` call. Also supports `reformat`,

	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.

	 * @param  string method Can be used to specify the type of picker to be added

	 * @return jQuery

	 */

	$.timepicker.dateRange = function(startTime, endTime, options, method) {

		method = method || 'datepicker';

		$.timepicker.handleRange(method, startTime, endTime, options);

	};



	/**

	 * Calls `method` on the `startTime` and `endTime` elements, and configures them to

	 * enforce date range limits.

	 * @param  string method Can be used to specify the type of picker to be added

	 * @param  Element startTime

	 * @param  Element endTime

	 * @param  obj options Options for the `timepicker()` call. Also supports `reformat`,

	 *   a boolean value that can be used to reformat the input values to the `dateFormat`.

	 * @return jQuery

	 */

	$.timepicker.handleRange = function(method, startTime, endTime, options) {

		$.fn[method].call(startTime, $.extend({

			onClose: function(dateText, inst) {

				checkDates(this, endTime, dateText);

			},

			onSelect: function(selectedDateTime) {

				selected(this, endTime, 'minDate');

			}

		}, options, options.start));

		$.fn[method].call(endTime, $.extend({

			onClose: function(dateText, inst) {

				checkDates(this, startTime, dateText);

			},

			onSelect: function(selectedDateTime) {

				selected(this, startTime, 'maxDate');

			}

		}, options, options.end));

		// timepicker doesn't provide access to its 'timeFormat' option, 

		// nor could I get datepicker.formatTime() to behave with times, so I

		// have disabled reformatting for timepicker

		if (method != 'timepicker' && options.reformat) {

			$([startTime, endTime]).each(function() {

				var format = $(this)[method].call($(this), 'option', 'dateFormat'),

					date = new Date($(this).val());

				if ($(this).val() && date) {

					$(this).val($.datepicker.formatDate(format, date));

				}

			});

		}

		checkDates(startTime, endTime, startTime.val());



		function checkDates(changed, other, dateText) {

			if (other.val() && (new Date(startTime.val()) > new Date(endTime.val()))) {

				other.val(dateText);

			}

		}

		selected(startTime, endTime, 'minDate');

		selected(endTime, startTime, 'maxDate');



		function selected(changed, other, option) {

			if (!$(changed).val()) {

				return;

			}

			var date = $(changed)[method].call($(changed), 'getDate');

			// timepicker doesn't implement 'getDate' and returns a jQuery

			if (date.getTime) {

				$(other)[method].call($(other), 'option', option, date);

			}

		}

		return $([startTime.get(0), endTime.get(0)]);

	};



	/*

	* Keep up with the version

	*/

	$.timepicker.version = "1.1.2";



})(jQuery);

